﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace ReporterEngine
{
    public class Template
    {
        private TemplateNode root;

        public List<TemplateNode> ErrorNodes = new List<TemplateNode>();

        public bool HasError
        {
            get { return ErrorNodes.Count > 0; }
        }

        public TemplateNode Root
        {
            get { return root; }
            set { root = value; }
        }

       //cahced templates
        public static Dictionary<string,Template> CachedTemplates = new Dictionary<string, Template>();
        
        public static Template Parse(XElement contentElement)
        {
            var Key = contentElement.ToString();

            if (CachedTemplates.ContainsKey(Key))
                return CachedTemplates[Key];

            var content = contentElement.Value;

            Template tpl = new Template();
            tpl.Root = new TemplateNode{CommandType = CommandTypes.Root};

            var currentNode = tpl.Root;

            

            StringBuilder sb = new StringBuilder();

            Stack<TemplateNode> sectionStack = new Stack<TemplateNode>();

            var match = WordReportEngine.CommandExp.Match(content);

            if(!match.Success)
                return tpl;

            var startIndex = match.Index;//content.IndexOfAny(new char[] { '{' });

            while(startIndex >= 0)
            {
                var endCommandNameIndex = content.IndexOfAny(new char[] {':', '<', '>', '}'}, startIndex);
                var endIndex = content.IndexOfAny(new char[] { '}' }, startIndex);

                var commandName = content.Substring(startIndex + 1, endCommandNameIndex - startIndex - 1);
                commandName = commandName.Replace(" ", "").Replace("\n", "").Replace("\t", "");

                CommandTypes command = CommandTypes.Undefined;

                try
                {
                    command = (CommandTypes)Enum.Parse(typeof(CommandTypes), commandName, true);
                }
                catch (Exception e)
                {
                    command = CommandTypes.Undefined;
                }

                var node = new TemplateNode { CommandType = command, StartPosition = startIndex, EndPosition = endIndex, Parent = currentNode};
                node.StringExpression = content.Substring(startIndex + 1, endIndex - startIndex - 1);

                //parse command by regex

                //if(node.StringExpression.Contains("<<"))
                //{
                //    int ee2 = 12;
                //}

                var matchResult = WordReportEngine.PureCommandExp.Match(node.StringExpression);

                var commandIndex = matchResult.Index;

                var lowerCommandName = matchResult.Groups["command"].Value.Trim();
                var sectionname = matchResult.Groups["sectionname"].Value;
                var query = matchResult.Groups["query"].Value;


                if (!string.IsNullOrEmpty(commandName))
                    lowerCommandName = commandName.ToLower();

                node.Command.CommandName = lowerCommandName;
                node.Command.SectionName = sectionname;
                node.Command.Query = query.Trim();
                node.Command.Index = commandIndex;



                if (!node.Command.Query.StartsWith(">>") && !node.Command.Query.StartsWith("<<"))
                {
                    if (node.Command.Query.StartsWith(">"))
                        node.Command.Query = ">" + node.Command.Query;
                    else if( node.Command.Query.StartsWith("<"))
                         node.Command.Query = "<" + node.Command.Query;
                    else
                     node.Command.Query = ">> " + node.Command.Query;
                }

                //parse command by regex

                if (node.CommandType == CommandTypes.Section)
                {
                    int ee = 12;
                }

                //exclude from row or list if position is after element
                while (sectionStack.Count > 0 && sectionStack.Peek().EndGroupCommandPosition != 0 && sectionStack.Peek().EndGroupCommandPosition <= startIndex)
                {
                    var lastRow = sectionStack.Pop();

                    //lastRow.Parent.Childs.Add(node);

                    currentNode = lastRow.Parent;
                    //set end section command
                    // currentNode.EndSectionNode = node;
                }


                currentNode.Childs.Add(node);

                if (node.CommandType == CommandTypes.Section)
                {
                    sectionStack.Push(node);

                    node.Parent = currentNode;

                    currentNode = node;
                }

                if (node.CommandType == CommandTypes.Row)
                {
                    var startRowPosition = 0;
                    var rowEl = WordReportEngine.GetElementByTextIndex(contentElement, startIndex, out startRowPosition);
                    //find trEl and start position
                    var trEl = WordReportEngine.ClosestParent(rowEl, x => x.Name.LocalName == "tr");

                    var endRowPosition = startRowPosition + trEl.Value.Length;
                    node.EndGroupCommandPosition = endRowPosition;
                    
                    sectionStack.Push(node);

                    node.Parent = currentNode;

                    currentNode = node;
                }

                if (node.CommandType == CommandTypes.EndSection)
                {
                    if (sectionStack.Count == 0)
                        tpl.ErrorNodes.Add(currentNode);
                    else
                    {
                        currentNode = sectionStack.Pop();

                        //set end section command
                        currentNode.EndSectionNode = node;

                        if (sectionStack.Count == 0)
                            currentNode = tpl.root;
                    }
                }
                

                startIndex = content.IndexOfAny(new char[] { '{' },endIndex);
            }
            
            //CachedTemplates.Add(Key,tpl);

            return tpl;
        }

        public List<string> GetParameters()
        {
            var result = new List<string>();

            IterateAll(this.Root, result);

            return result;
        }

        void IterateAll(TemplateNode node, List<string> parameters)
        {
            //.Groups["command"].Value.Trim();
            if (!String.IsNullOrEmpty(node.Command.Query))
            {
                var paramMatches = WordReportEngine.ParamExp.Matches(node.Command.Query);

                foreach (Match paramMatch in paramMatches)
                {
                    var paramName = paramMatch.Groups["param"].Value.Trim().ToLower();

                    if (!parameters.Contains(paramName))
                        parameters.Add(paramName);
                }
            }

            foreach (var innerNode in node.Childs)
            {
                IterateAll(innerNode, parameters);
            }
        }
    }
}
